package com.cti.ftpservice.utils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.codec.binary.Base64;


public class Base64CoderUtils {

	/**
	 * 对obj对象的String字段进行base64编码，采用utf-8格式
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	@Deprecated
	public static Object encoderObj(Object obj) throws Exception{
		return encoderObj(obj,null);
	}
	/**
	 * 对obj对象的String字段进行base64编码
	 * 
	 * @param obj
	 * @param excludeFieldNames
	 * @return
	 * @throws Exception 
	 */
	public static Object encoderObj(Object obj, String[] excludeFieldNames) throws Exception {
		Object newObject = null;
		try {

			newObject = obj.getClass().newInstance();
			List<Field> fields = getObjectAllDeclaredFields(obj);
			for (Field field : fields) {
				String fieldName = field.getName();
				if (!field.getType().getName().equals(String.class.getName())) {
					// 直接赋值
					Object value = getFieldValue(obj, field);
					setFieldValue(newObject, field, value);
					continue;
				}

				boolean isRequiredEncoder = isContainInArray(fieldName,
						excludeFieldNames);
				if (isRequiredEncoder) {
					// 包含，直接赋值
					Object value = getFieldValue(obj, field);
					setFieldValue(newObject, field, value);
				} else {
					// 不包含，转换为base64编码
					Object value = getFieldValue(obj, field);
					String newValue = (String) value;
					if (value != null) {
						newValue = encoderString((String) value);
					}
					setFieldValue(newObject, field, newValue);
				}
			}
		} catch (Exception e) {
			throw e;
		} 
		return newObject;
	}


	/**
	 * base64编码
	 * @param value
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public static String encoderString(String value) throws UnsupportedEncodingException {
		return 	encoderString(value, "utf-8");
	}
	
	/**
	 * 编码
	 * @param value
	 * @param charset
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public static String encoderString(String value,String charset) throws UnsupportedEncodingException {
		if(value==null||"".equals(value))
			return null;
		
		return Base64.encodeBase64String(value.getBytes(charset));
	}
	
	/**
	 * 解码
	 * @param value
	 * @return
	 * @throws IOException 
	 */
	public static String decoderString(String value) throws IOException  {
		return decoderString(value,"utf-8");
	}
	
	public static Object encoderObj2(Object obj,String... ignoreFieldNames) throws Exception {
		return encoderObj2(obj, ignoreFieldNames,"utf-8");
	}

	/**
	 * 对obj对象的String字段进行base64编码，采用utf-8格式
	 * 
	 * @param obj
	 * @param excludeFieldNames
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings({ "rawtypes", "unused", "unchecked" })
	public static Object encoderObj2(Object obj,String[] ignoreFieldNames, String charset)
			throws Exception {
		if (obj == null)
			return null;
		Class<?> cls = obj.getClass();
		if (cls == String.class) {
			return encoderString((String) obj, charset);
		}

		if (isSimpleType(obj)) {
			return obj;
		}
		
		if(obj instanceof Date){
			return ((Date) obj).clone();
		}

		// 数组
		if (cls.isArray()) {
			Class<?> componentType = cls.getComponentType();
			int arrayLength = Array.getLength(obj);
			Object targetObj = Array.newInstance(componentType, arrayLength);
			for (int i = 0; i < arrayLength; i++) {
				Object element = Array.get(obj, i);
				Object encodeElement = encoderObj2(element,ignoreFieldNames, charset);
				Array.set(targetObj, i, encodeElement);
			}
			return targetObj;
		}

		// Collection
		if (obj instanceof Collection) {
			Object targetObj = cls.newInstance();
			Iterator iterator = ((Collection) obj).iterator();
			int size = ((Collection) obj).size();
			if (size > 0) {
				for (int j = 0; iterator.hasNext(); j++) {
					Object element = iterator.next();
					Object encodeElement = encoderObj2(element,ignoreFieldNames, charset);
					((Collection) targetObj).add(encodeElement);
				}
			}
			return targetObj;
		}

		// Map
		if (obj instanceof Map) {
			Object targetObj = cls.newInstance();
			Set keySet = ((Map) obj).keySet();
			Iterator iterator = keySet.iterator();
			int size = keySet.size();
			if (size > 0) {
				for (int i = 0; iterator.hasNext(); i++) {
					Object key = iterator.next();
					Object value = ((Map) obj).get(key);

					Object encodeKey = encoderObj2(key, ignoreFieldNames,charset);
					Object encodeValue = encoderObj2(value, ignoreFieldNames,charset);

					((Map) targetObj).put(encodeKey, encodeValue);
				}
			}
			return targetObj;
		}

		// 类类型，复杂的Class类型
		Object targetObject = obj.getClass().newInstance();
		List<Field> fields = getObjectAllDeclaredFields(obj);
		List<String> ignoreList = (ignoreFieldNames != null) ? Arrays.asList(ignoreFieldNames) : null;
		for (Field field : fields) {
			if(ignoreList!=null && ignoreList.contains(field.getName()))
				continue;
			Method getMethod=getGetFieldMethod(obj, field);
			if(getMethod==null)
				continue;
			Method setMethod=getSetFieldMethod(obj, field);
			if(setMethod==null)
				continue;
			Object value=getMethod.invoke(obj,  new Object[]{});
			Object encodValue=encoderObj2(value,ignoreFieldNames,charset);
			setMethod.invoke(targetObject, new Object[]{encodValue});
		}

		return targetObject;

	}
	
	/**
	 * 判断是否是复杂类型
	 * 
	 * @param obj
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public static boolean isComplexType(Object obj) {

		if (obj instanceof Boolean || obj instanceof Short
				|| obj instanceof Byte || obj instanceof Integer
				|| obj instanceof Long || obj instanceof Float
				|| obj instanceof Character || obj instanceof Double) {

			return false;
		} else {
			Class objectClass = obj.getClass();

			if (objectClass == boolean.class || objectClass == Boolean.class
					|| objectClass == short.class || objectClass == Short.class
					|| objectClass == byte.class || objectClass == Byte.class
					|| objectClass == int.class || objectClass == Integer.class
					|| objectClass == long.class || objectClass == Long.class
					|| objectClass == float.class || objectClass == Float.class
					|| objectClass == char.class
					|| objectClass == Character.class
					|| objectClass == double.class
					|| objectClass == Double.class || objectClass.isEnum()) {
				return false;
			}

			else {
				return true;
			}
		}
	}

	/**
	 * 判断是否是简单类型
	 * 
	 * @param obj
	 * @return
	 */
	public static boolean isSimpleType(Object obj) {

		return !isComplexType(obj);
	}

	/**
	 * 获取包含其父类的所有Field
	 * 
	 * @param obj
	 * @return
	 */
	private static List<Field> getObjectAllDeclaredFields(Object obj) {
		List<Field> list = new ArrayList<Field>();
		Class<?> classType = obj.getClass();
		for (; classType != Object.class; classType = classType.getSuperclass()) {
			Field[] fields = classType.getDeclaredFields();
			if (fields != null && fields.length > 0) {
				for (Field field : fields) {
					list.add(field);
				}
			}
		}
		return list;
	}
	
	private static Method getSetFieldMethod(Object obj, Field field){
		String methodName = "set"
				+ field.getName().substring(0, 1).toUpperCase()
				+ field.getName().substring(1);
		Method method = getMethod(obj, methodName,
				new Class<?>[] { field.getType() });
		return method;
	}
	
	private static Method getGetFieldMethod(Object obj, Field field)
			throws IllegalAccessException, IllegalArgumentException,
			InvocationTargetException, NoSuchMethodException, SecurityException {
		String methodName = "get"
				+ field.getName().substring(0, 1).toUpperCase()
				+ field.getName().substring(1);
		Method method = getMethod(obj, methodName, new Class[] {});
		return method;
	}
	
	
	/**
	 * 解码
	 * @param value
	 * @param charset
	 * @return
	 * @throws IOException 
	 */
	public static String decoderString(String value,String charset) throws IOException {
		if(value==null||"".equals(value))
			return null;
		return new String(Base64.decodeBase64(value),charset);
	}

	/**
	 * 设置Field的值
	 * @param obj
	 * @param fieldName
	 * @param value
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 */
	private static void setFieldValue(Object obj, Field field,
			Object value) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, NoSuchFieldException {
		Method method=getSetFieldMethod(obj, field);
		if(method==null)
			return;
		method.invoke(obj, new Object[]{value});
	}

	/**
	 * 根据方法名称获取一个方法，可以从父类获取
	 * @param obj
	 * @param methodName
	 * @return
	 */
	private static Method getMethod(Object obj, String methodName,Class<?>[] parameterTypes) {
		Class<?> classType=obj.getClass();
		for(;classType!=Object.class;classType=classType.getSuperclass()){
			try {
				Method method=classType.getMethod(methodName, parameterTypes);
				return method;
			} catch (Exception e) {
			}
		}
		return null;
	}

	/**
	 * 获取Field的值
	 * @param obj
	 * @param fieldName
	 * @return
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 */
	private static Object getFieldValue(Object obj, Field field) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		Method method=getGetFieldMethod(obj, field);
		if(method==null)
			return null;
		return method.invoke(obj, new Object[]{});
	}

	private static boolean isContainInArray(String fieldName,
			String[] excludeFieldNames) {

		if (excludeFieldNames != null && excludeFieldNames.length > 0) {
			for (String excludeFieldName : excludeFieldNames) {
				if (excludeFieldName.toLowerCase().equals(
						fieldName.toLowerCase())) {
					return true;
				}
			}
		}
		return false;
	}
	
	/**
	 * 对object类型中的string类型的变量用base64解码  utf-8编码
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	@Deprecated
	public static Object decoderObj(Object obj) throws Exception {
		return decoderObj(obj, null);
	}

	/**
	 * 对object类型中的string类型的变量用base64解码
	 * 
	 * @param obj
	 * @param strings
	 * @return
	 * @throws Exception 
	 */
	@Deprecated
	public static Object decoderObj(Object obj, String[] excludeFieldNames) throws Exception {
		Object newObject = null;
		try {

			newObject = obj.getClass().newInstance();
			List<Field> fields = getObjectAllDeclaredFields(obj);
			for (Field field : fields) {
				String fieldName = field.getName();
				if (!field.getType().getName().equals(String.class.getName())) {
					// 直接赋值
					Object value = getFieldValue(obj, field);
					setFieldValue(newObject, field, value);
					continue;
				}

				boolean isNotRequiredEncoder = isContainInArray(fieldName,
						excludeFieldNames);
				if (isNotRequiredEncoder) {
					// 包含，直接赋值
					Object value = getFieldValue(obj, field);
					setFieldValue(newObject, field, value);
				} else {
					// 不包含，base64解码
					Object value = getFieldValue(obj, field);
					String newValue = (String) value;
					if (value != null) {
						newValue = decoderString((String) value);
					}
					setFieldValue(newObject, field, newValue);
				}
			}
		} catch (Exception e) {
			throw e;
		} 
		return newObject;
		
	}
	
	@Deprecated
	public static <T> List<T> decoderList(List<T> datas) throws Exception {
		return decoderList(datas, null);
	}
	
	@Deprecated
	@SuppressWarnings("unchecked")
	public static <T> List<T> decoderList(List<T> datas,String[] excludeFieldNames) throws Exception {
		List<T> result=new ArrayList<T>();
		if(datas==null||datas.size()==0)
			return result;
		for (T t : datas) {
			T decode=(T) decoderObj(t,excludeFieldNames);
			result.add(decode);
		}
		return result;
	}
	
	public static Object decoderObj2(Object obj,String... ignoreFiledNames) throws Exception {
		return decoderObj2(obj, ignoreFiledNames,"utf-8");
	}

	/**
	 * 对obj对象的String字段进行base64编码，采用utf-8格式
	 * 
	 * @param obj
	 * @param excludeFieldNames
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings({ "rawtypes", "unused", "unchecked" })
	public static Object decoderObj2(Object obj,String[] ignoreFiledNames, String charset)
			throws Exception {
		if (obj == null)
			return null;
		Class<?> cls = obj.getClass();
		if (cls == String.class) {
			return decoderString((String) obj, charset);
		}

		if (isSimpleType(obj)) {
			return obj;
		}

		if(obj instanceof Date){
			return ((Date) obj).clone();
		}

		// 数组
		if (cls.isArray()) {
			Class<?> componentType = cls.getComponentType();
			int arrayLength = Array.getLength(obj);
			Object targetObj = Array.newInstance(componentType, arrayLength);
			for (int i = 0; i < arrayLength; i++) {
				Object element = Array.get(obj, i);
				Object decodeElement = decoderObj2(element, ignoreFiledNames,charset);
				Array.set(targetObj, i, decodeElement);
			}
			return targetObj;
		}

		// Collection
		if (obj instanceof Collection) {
			Object targetObj = cls.newInstance();
			Iterator iterator = ((Collection) obj).iterator();
			int size = ((Collection) obj).size();
			if (size > 0) {
				for (int j = 0; iterator.hasNext(); j++) {
					Object element = iterator.next();
					Object decodeElement = decoderObj2(element,ignoreFiledNames, charset);
					((Collection) targetObj).add(decodeElement);
				}
			}
			return targetObj;
		}

		// Map
		if (obj instanceof Map) {
			Object targetObj = cls.newInstance();
			Set keySet = ((Map) obj).keySet();
			Iterator iterator = keySet.iterator();
			int size = keySet.size();
			if (size > 0) {
				for (int i = 0; iterator.hasNext(); i++) {
					Object key = iterator.next();
					Object value = ((Map) obj).get(key);

					Object decodeKey = decoderObj2(key,ignoreFiledNames, charset);
					Object decodeValue = decoderObj2(value,ignoreFiledNames, charset);

					((Map) targetObj).put(decodeKey, decodeValue);
				}
			}
			return targetObj;
		}

		// 类类型，复杂的Class类型
		Object targetObject = obj.getClass().newInstance();
		List<Field> fields = getObjectAllDeclaredFields(obj);
		List<String> ignoreList = (ignoreFiledNames != null) ? Arrays.asList(ignoreFiledNames) : null;
		for (Field field : fields) {
			field.setAccessible(true);
			if(ignoreList!=null && ignoreList.contains(field.getName()))
				continue;
			Object value=field.get(obj);
			Object decodValue=decoderObj2(value,ignoreFiledNames,charset);
			field.set(targetObject, decodValue);
		}

		return targetObject;

	}
	
}
